#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <numa.h>
#include <pthread.h>
#include <semaphore.h>
#include "param.h"
#include "types.h"
#include "defs.h"

extern int numa_opt;

/* 将文件绑定至buffer,成功返回buffer指针，否则返回NULL */
filebuf *
filebuf_init (int fd, enum fb_type type, int bnum ,size_t bsize, int numa_node) {
    filebuf *fbuf;
    buf *b, *prev, *next;
    int i;
    fbuf = (filebuf *) malloc (sizeof (filebuf));
    if (fbuf == NULL)
        HANDLE_ERROR ("get_filebuf","filebuf",1);
    fbuf->type = type;
    fbuf->status = FB_S_READY;
    fbuf->b_in = NULL;
    fbuf->b_out = NULL;
    fbuf->b_un = NULL;
    fbuf->fd = fd;
    for (i = 0; i < bnum; i++) {
        if ((b = (buf *) malloc (sizeof (buf))) == NULL)
            HANDLE_ERROR ("get_filebuf","buf",1);
        b->curr = 0;
        b->elem_num = (bsize / sizeof (uint64));
        if (numa_opt) {
            if((b->data = numa_alloc_onnode (bsize, numa_node)) == NULL) 
                HANDLE_ERROR ("get_filebuf","buf->data",1);
        } else {
            if((b->data = malloc (bsize)) == NULL) 
                HANDLE_ERROR ("get_filebuf","buf->data",1);
        }
        if (fbuf->b_in == NULL) {
            b->next = b;
            b->prev = b;
        } else {
            prev = fbuf->b_in->prev;
            next = fbuf->b_in;
            b->next = next;
            b->prev = prev;
            next->prev = b;
            prev->next = b;
        }
        fbuf->b_in = b;
    }
    if (type == FB_T_RD)
        sem_init (&(fbuf->ex_res), 0, 0);
    else if (type == FB_T_WR)
        sem_init (&(fbuf->ex_res), 0, OUTPUT_BNUM);
    else
        sem_init (&(fbuf->ex_res), 0, 0);
    pthread_mutex_init (&(fbuf->mtx), NULL);
    __asm__ __volatile__("": ::"memory");
    return fbuf;
    error:
    return NULL;
}

static int
buf_remove_ring (buf* head) {
    buf *b_ptr, *b_tmp, *b_start;
    if (head == NULL)
        HANDLE_ERROR ("buf_remove_ring","head",0);
    b_ptr = head;
    b_start = head;
    while (b_ptr->next != b_start) {
        free (b_ptr->data);
        b_tmp = b_ptr;
        b_ptr = b_ptr->next;
        free (b_tmp);        
    }
    /* 最后一个 */
    if (b_ptr->next == b_start) {
        free (b_ptr->data);
        free (b_ptr);
    }
    return 0;
    error:
    return -1;
}

int
filebuf_destory(filebuf* fbuf) {
    if (fbuf->status != FB_S_FIN) {
        HANDLE_ERROR ("filebuf_destory", "fbuf->status",0);
    }
    if (fbuf->b_in != NULL)
        buf_remove_ring (fbuf->b_in);
    if (fbuf->b_out != NULL)
        buf_remove_ring (fbuf->b_out);
    if (fbuf->b_un != NULL)
        buf_remove_ring (fbuf->b_un);
    pthread_mutex_destroy (&(fbuf->mtx));
    sem_destroy (&(fbuf->ex_res));
    free (fbuf);
    return 0;
    error:
    return -1;
}

#define glue(x,y) x##y
#define xglue(w,x,y,z) w##x##y##z

#define filebuf_trans(x,y) \
int \
xglue(filebuf_trans_,x,2,y)(filebuf *fbuf){\
    buf * curr_ptr, *prev_ptr, *next_ptr;\
    LOCK (fbuf->mtx);\
    if (fbuf->glue(b_,x) == NULL)\
        HANDLE_ERROR ("filebuf_trans","fbuf->",0);\
    curr_ptr = fbuf->glue(b_,x);\
    if (curr_ptr->next == curr_ptr) {\
        fbuf->glue(b_,x) = NULL;\
    } else {\
        prev_ptr = fbuf->glue(b_,x)->prev;\
        next_ptr = fbuf->glue(b_,x)->next;\
        prev_ptr->next = next_ptr;\
        next_ptr->prev = prev_ptr;\
        fbuf->glue(b_,x) = next_ptr;\
    }\
    if (fbuf->glue(b_,y) == NULL) {\
        curr_ptr->next = curr_ptr;\
        curr_ptr->prev = curr_ptr;\
        fbuf->glue(b_,y) = curr_ptr;\
    } else {\
        prev_ptr = fbuf->glue(b_,y)->prev;\
        next_ptr = fbuf->glue(b_,y);\
        prev_ptr->next = curr_ptr;\
        curr_ptr->prev = prev_ptr;\
        curr_ptr->next = next_ptr;\
        next_ptr->prev = curr_ptr;\
    }\
    UNLOCK (fbuf->mtx);\
    error:\
    UNLOCK (fbuf->mtx);\
    return -1;\
}

filebuf_trans(in,out);
filebuf_trans(out,in);
filebuf_trans(in,un);
filebuf_trans(out,un);
